package com.chunlin.polymrphism;

public class polymrphism {
    //在java中要实现多态，必须要满足如下几个条件，缺一不可：
    //1. 必须在继承体系下
    //2. 子类必须要对父类中方法进行重写
    //3. 通过父类的引用调用重写的方法
    //多态体现：在代码运行时，当传递不同类对象时，会调用对应类中的方法。
    public static void eat(Animal animal) {
        // 编译器在编译代码时，并不知道要调用Dog 还是 Cat 中eat的方法
        // 等程序运行起来后，形参a引用的具体对象确定后，才知道调用那个方法
        // 注意：此处的形参类型必须时父类类型才可以
        animal.eat();
    }

    public static void test1() {
        Cat cat = new Cat("xiaolei", 25);
        Dog dog = new Dog("xinhang", 23);

        eat(cat);
        eat(dog);
    }

    public static void test2() {
        Cat cat = new Cat("xiaolei", 25);
        Dog dog = new Dog("xinhang", 23);

        //向上转型
        //向上转型的优点：让代码实现更简单灵活
        //向上转型的缺陷：不能调用到子类特有的方法
        Animal animal = cat;
        animal.eat();
        animal = dog;
        animal.eat();

        //将一个子类对象经过向上转型之后当成父类方法使用，再无法调用子类的方法
        // 但有时候可能需要调用子类特有的方法，此时：将父类引用再还原为子类对象即可，即向下转换
        // 编译失败，编译时编译器将animal当成Animal对象处理
        // 而Animal类中没有bark方法，因此编译失败
        // animal.bark();

        // 向上转型
        // 程序可以通过编程，但运行时抛出异常---因为：animal实际指向的是狗
        // 现在要强制还原为猫，无法正常还原，运行时抛出：ClassCastException
        // cat = (Cat)animal;
        // cat.mew();

        // animal本来指向的就是狗，因此将animal还原为狗也是安全的
        dog = (Dog)animal;
        dog.bark();

        //向下转型用的比较少，而且不安全，万一转换失败，运行时就会抛异常。Java中为了提高向下转型的安全性
        // 引入了instanceof ，如果该表达式为true，则可以安全转换
        if (animal instanceof Cat) {
            cat = (Cat)animal;
            cat.mew();
        }

        if (animal instanceof Dog) {
            dog = (Dog)animal;
            dog.bark();
        }
    }

    //多态缺陷：代码的运行效率降低
    //1. 属性没有多态性
    public static void test3() {
        // 当父类和子类都有同名属性的时候，通过父类引用，只能引用父类自己的成员属性
        //
        Parent parent = new Child();
        //属性的访问是在编译时绑定的（静态绑定）。编译器根据引用变量的声明类型来决定访问哪个属性
        System.out.println(parent.name);
        //方法调用是在运行时绑定的（动态绑定）。JVM根据对象的实际类型来决定调用哪个方法
        parent.display();
    }
    //2.构造方法没有多态性
    //避免在构造方法中调用重写的方法
    public static void test4() {
        B b = new B();

        //构造 D 对象的同时, 会调用 B 的构造方法
        //B 的构造方法中调用了 func 方法, 此时会触发动态绑定, 会调用到 D 中的 func
        //此时 D 对象自身还没有构造, 此时 num 处在未初始化的状态, 值为 0
        // 如果具备多态性，num的值应该是1
        //所以在构造函数内，尽量避免使用实例方法，除了final和private方法
        //结论: "用尽量简单的方式使对象进入可工作状态", 尽量不要在构造器中调用方法(如果这个方法被子类重写
        // 就会触发动态绑定, 但是此时子类对象还没构造完成), 可能会出现一些隐藏的但是又极难发现的问题
    }

    public static void main(String[] args) {
//        test1();
//        test2();
//        test3();
        test4();
    }
}
